home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
scheme
/
boxer
/
boxer.lha
/
bind.lisp
next >
Wrap
Lisp/Scheme
|
1993-07-17
|
14KB
|
331 lines
;; -*- Mode:LISP; Package:(BOXER GLOBAL 1000); Base:10.; Fonts:CPTFONT -*-
;;
;; (C) Copyright 1983 MIT
;;
;; Permission to use, copy, modify, distribute, and sell this software
;; and its documentation for any purpose is hereby granted without fee,
;; provided that the above copyright notice appear in all copies and that
;; both that copyright notice and this permission notice appear in
;; supporting documentation, and that the name of M.I.T. not be used in
;; advertising or publicity pertaining to distribution of the software
;; without specific, written prior permission. M.I.T. makes no
;; representations about the suitability of this software for any
;; purpose. It is provided "as is" without express or implied warranty.
;;
;;
;; Deep Binding in Boxer.
;;Dynamic Boxer variables exist in an alist. You get the value of a
;;variable by calling the lookup function on it.
;;
;;If the variable is not found in the alist, then the static variables of the boxes in
;;the lexical scope of the outermost box being executed are searched. This searching
;;happens by asking the DOIT'ed box to look up the variable in its static
;;alist, and failing finding it there to ask the box it is inside of to do the same,
;;all the way to the toplevel box.
;;If this search fails, then the lookup function checks the global lispm value cell
;;of the symbol. This keeps it from having to search a long ``tail'' of primitive
;;values.
;;FUNCTION CALLING.
;;When a function is called, the funcalling mechanism boxer-binds the input variables of the
;;box being called to be the argument values. It does this by lisp-binding the big alist
;;to be a cons of those variable names and values on the front of
;;the big alist. This lisp binding goes away when the funcall primitive returns.
;;
;;In addition to the input variables, then alist of static variables for the current box
;;is copied and added to the big alist temporary binding. It is copied since in our
;;copy-and-execute model, modifications to the static bindings of a box made while the
;;box is being are not retained when the box returns.
;;***this is not yet implemented***
;;
;; TELL
;;TELL binds *BOXER-BINDING-ALIST-ROOT* to NIL (to hide any dynamic bindings)
;;and binds *BOXER-BINDING-ALIST-ROOT* to box being told.
(deff boxer-error 'ferror)
(defvar *currently-executing-box* nil
"BOXER-FUNCALL binds this to the box it is funcalling.")
(DEFVAR *BOXER-STATIC-VARIABLES-ROOT* NIL
"The DOIT key binds the box whose region is being run to be this box.")
(DEFMACRO WITH-STATIC-ROOT-BOUND (NEW-ROOT &BODY BODY)
`(LET ((*BOXER-STATIC-VARIABLES-ROOT* ,NEW-ROOT))
. ,BODY))
(DEFVAR *BOXER-DYNAMIC-VARIABLES-ALIST* NIL)
(DEFMACRO WITH-DYNAMIC-VALUES-BOUND (NEW-FRAME &BODY BODY)
`(LET ((*BOXER-DYNAMIC-VARIABLES-ALIST*
(ADJOIN-FRAME ,NEW-FRAME *BOXER-DYNAMIC-VARIABLES-ALIST*)))
. ,BODY))
(DEFMACRO WITH-NEW-DYNAMIC-VALUES (NEW-FRAME &BODY BODY)
`(LET ((*BOXER-DYNAMIC-VARIABLES-ALIST* (ADJOIN-FRAME ,NEW-FRAME NIL)))
. ,BODY))
(defmacro boxer-let* (bindings &body body)
`(let ((*boxer-binding-alist-root*
(nconc (mapcar #'(lambda (pair)
(cons (car pair)
(eval (cadr pair))))
',bindings)
*boxer-binding-alist-root*)))
.,body))
;;Handling the dynamic environment
;;; this need to flatten out any exporting boxes (SLOW !!!)
;;; The whole exporting scheme needs to be re-implemented for speed
;;; and here's an example why....
(DEFUN GET-LOCAL-ENV (BOX)
(COND ((BOX? BOX)
(LET* ((BINDINGS (TELL BOX :GET-STATIC-VARIABLES-ALIST))
(EXPORTS (MAPCAR #'CDR
(SUBSET #'(LAMBDA (X) (EQ (CAR X) *EXPORTING-BOX-MARKER*))
BINDINGS)))
(parsed-bindings (with-collection
(dolist (b bindings)
(unless (eq (car b) *exporting-box-marker*)
(collect b))))))
(LEXPR-FUNCALL #'APPEND parsed-bindings
(MAP-TELL EXPORTS :GET-STATIC-VARIABLES-ALIST))))
((NUMBERP BOX) NIL)
(T (EVBOX-BINDINGS BOX))))
;;; This is doing EXPLICIT copying of local variables because we are only copying the args and
;;; NOT the function itself whenever we funcall
(DEFSUBST MAKE-FRAME (BOX &OPTIONAL ARGS)
(NCONC (NCONS (CONS :FRAME-HEADER BOX))
(PAIRLIS ;side effects are safe because of
(GET-ARG-NAMES BOX) ;PAIRLIS
ARGS)
(LET ((*EVALUATOR-COPYING-FUNCTION* #'SHALLOW-COPY-FOR-ARGLIST))
(MAPCAR #'(LAMBDA (X) (CONS (CAR X) (COPY-FOR-EVAL (CDR X))))
(GET-LOCAL-ENV BOX)))))
(DEFSUBST ADJOIN-FRAME (FRAME ENV)
(APPEND FRAME ENV))
;;Variable lookup function
;; note that box can be an EVbox
(defun lookup-static-variable (variable box)
(cond ((box? box) (tell box :lookup-static-variable-check-superiors variable))
((evbox? box) (assq variable (evbox-bindings box)))
(t (ferror "Don't know how to look up the variable, ~S, in ~S" variable box))))
(DEFUN BOXER-SYMEVAL (VARIABLE)
(LET ((ENTRY (ASSQ VARIABLE *BOXER-DYNAMIC-VARIABLES-ALIST*)))
(COND ((NOT (NULL ENTRY)) (CDR ENTRY))
((SETQ ENTRY (lookup-static-variable VARIABLE *BOXER-STATIC-VARIABLES-ROOT*))
(CDR ENTRY))
((BOUNDP VARIABLE) ;global primitive?
(SYMEVAL VARIABLE)) ;we cache them to avoid a long tail in the alist.
(T (BOXER-ERROR "The variable ~A is not bound." VARIABLE)))))
(DEFUN BOXER-BOUNDP (VARIABLE)
(or (assq variable *BOXER-DYNAMIC-VARIABLES-ALIST*)
(LOOKUP-STATIC-VARIABLE variable *BOXER-STATIC-VARIABLES-ROOT*)
(boundp variable))) ;global primitive?
;; local lookup function
;; This takes an alist and looks up the variable. If there are EXPORTS into the alist, then
;; we recurse through the alists of the exports as well
;; GET-NAMED uses this
;; Note that this is doing a depth first search of the exports (where we might actually want
;; a breadth first search
(DEFUN LOOKUP-LOCAL-VARIABLE (VAR ALIST)
(LET ((EXPORTS (SUBSET #'(LAMBDA (X) (EQ (CAR X) *EXPORTING-BOX-MARKER*)) ALIST))
(THING (CDR (ASSQ VAR ALIST))))
(IF (NOT (NULL THING)) THING
(DOLIST (EXPORT EXPORTS)
(LET ((VALUE (LOOKUP-LOCAL-VARIABLE VAR (GET-LOCAL-ENV (CDR EXPORT)))))
(WHEN (NOT (NULL VALUE)) (RETURN VALUE)))))))
;;; KEEP this around for the parser
;Variable setting function with searching. Errors if there is no such variable.
;Copied from lookup function.
;This is a low-level function. Note that sometimes variable "setting"
;is implemented as box-alteration.
;(defun boxer-set (variable value)
; (let ((entry (assq variable *BOXER-DYNAMIC-VARIABLES-ALIST*)))
; (cond ((access-pair? variable)
; (let ((*BOXER-STATIC-VARIABLES-ROOT* (boxer-eval (access-pair-superbox variable)))
; (*BOXER-DYNAMIC-VARIABLES-ALIST* NIL))
; (boxer-set (caar (get-pre-box-rows (access-pair-subbox variable))) value)))
; ((not (null entry)) (setf (cdr entry) value))
; (t (setq entry (tell *BOXER-STATIC-VARIABLES-ROOT*
; :LOOKUP-STATIC-VARIABLE-CHECK-SUPERIORS
; variable))
; (if (not (null entry))
; (setf (cdr entry) value)
; (boxer-error "The variable ~S is not bound." variable))))))
;;; Weird stuff.
;;; Since there's no consistency about EVBOX objects we'll just add this here.
(defun add-static-variable-to-evbox (evbox variable value)
(if (eq variable *exporting-box-marker*)
(add-static-variable-to-evbox-internal evbox variable value)
(let ((entry (assq variable (evbox-bindings evbox))))
(cond ((null entry)
(add-static-variable-to-evbox-internal evbox variable value))
(t (format t "Warning, replacing the old value of ~A" variable)
(setf (cdr entry) value))))))
(defun add-static-variable-to-evbox-internal (evbox variable value)
(set-evbox-bindings evbox (cons (cons variable value)
(evbox-bindings evbox))))
;;;Lower level methods.
;;;Adds the variable/value pair to the current box's static variable alist.
;;;Needs to be smart about altering the alist -- or maybe re-calculating it or something?
;;;This implementation is broken since you won't be able to access the variable after
;;;you use it.
(DEFMETHOD (BOX :SET-STATIC-VARIABLES-ALIST) (NEW-ALIST)
;; the file system uses this one.
(SETQ STATIC-VARIABLES-ALIST NEW-ALIST))
(DEFMETHOD (BOX :GET-STATIC-VARIABLES-ALIST) ()
;; the file system uses this one too.
STATIC-VARIABLES-ALIST)
(defun boxer-add-static-variable (variable value)
(tell (or *CURRENTLY-EXECUTING-BOX* *BOXER-STATIC-VARIABLES-ROOT*)
:ADD-STATIC-VARIABLE-PAIR variable value))
(defmethod (box :add-static-variable-pair) (variable value)
(let ((entry (assq variable static-variables-alist)))
(WHEN (AND (NOT-NULL (TELL SELF :LOOKUP-STATIC-VARIABLE-IN-BOX-ONLY VARIABLE))
(NEQ (CDR (TELL SELF :LOOKUP-STATIC-VARIABLE-IN-BOX-ONLY VARIABLE)) VALUE)
(NEQ VARIABLE *EXPORTING-BOX-MARKER*))
;; The name is already defined in the current box to be something else
(FORMAT T "Warning, replacing the old value of ~A "VARIABLE))
(WHEN (SPRITE-BOX? VALUE)
;; This is not the correct solution since you might want to keep
;; some named sprites private to the graphics box. This should
;; cause the average user to win most of the time though
(TELL SELF :EXPORT-VARIABLE VARIABLE))
(COND ((AND (NEQ VARIABLE *EXPORTING-BOX-MARKER*) (not (null entry)))
(setf (cdr entry) value))
((AND (EQ VARIABLE *EXPORTING-BOX-MARKER*) (EQ VALUE (CDR ENTRY))))
;;try and cut down on multiple copies of the same box being exported
(T (push (cons variable value) static-variables-alist)))))
(DEFMETHOD (BOX :REMOVE-ALL-STATIC-BINDINGS) (VALUE)
"Removes all the variables which may be bound to VALUE. "
(LOOP WITH NEW-EXPORTS = NIL
FOR PAIR IN STATIC-VARIABLES-ALIST
UNLESS (EQ (CDR PAIR) VALUE)
COLLECT PAIR INTO NEW-ALIST
WHEN (AND (LISTP EXPORTS) (EQ (CDR PAIR) VALUE))
DO (SETQ NEW-EXPORTS (DELQ (CAR PAIR) EXPORTS))
FINALLY (SETQ STATIC-VARIABLES-ALIST NEW-ALIST)
(unless (eq exports *EXPORT-ALL-VARIABLES-MARKER*)
(setq EXPORTS NEW-EXPORTS))))
(DEFMETHOD (BOX :REMOVE-STATIC-VARIABLE) (VARIABLE)
"Removes only the single variable binding from the Box's environment. "
(SETQ STATIC-VARIABLES-ALIST (DELQ (ASSQ VARIABLE STATIC-VARIABLES-ALIST)
STATIC-VARIABLES-ALIST))
(WHEN (AND (NOT-NULL EXPORTS) (NEQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER*))
(SETQ EXPORTS (DELQ VARIABLE EXPORTS))))
(DEFMETHOD (BOX :SET-EXPORTS) (NEW-EXPORTS)
(SETQ EXPORTS NEW-EXPORTS))
(DEFMETHOD (BOX :GET-EXPORTS) ()
(IF (EQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER*)
(MAPCAR #'CAR STATIC-VARIABLES-ALIST)
EXPORTS))
(DEFMETHOD (BOX :EXPORT-ALL-VARIABLES) ()
(WHEN (NULL EXPORTS)
(TELL (TELL SELF :SUPERIOR-BOX) :ADD-STATIC-VARIABLE-PAIR *EXPORTING-BOX-MARKER* SELF))
(SETQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER*))
(DEFMETHOD (BOX :EXPORT-VARIABLE) (VARIABLE)
(LET ((VALUE (TELL SELF :LOOKUP-STATIC-VARIABLE-IN-BOX-ONLY VARIABLE)))
(UNLESS (NULL VALUE)
(WHEN (NULL EXPORTS)
(TELL (TELL SELF :SUPERIOR-BOX) :ADD-STATIC-VARIABLE-PAIR
*EXPORTING-BOX-MARKER* SELF))
(UNLESS (EQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER*)
(PUSH VARIABLE EXPORTS)))))
(DEFMETHOD (BOX :GET-EXPORTING-BOXES) ()
"Get a list of all the other boxes which export their variable bindings to this one. "
(MAPCAR #'CDR (SUBSET #'(LAMBDA (X) (EQ (CAR X) *EXPORTING-BOX-MARKER*))
STATIC-VARIABLES-ALIST)))
(DEFMETHOD (BOX :LOOKUP-STATIC-VARIABLE-IN-EXPORTS) (VARIABLE)
(LET ((EXPORTING-BOXES (TELL SELF :GET-EXPORTING-BOXES))
(EXPORTING-P (OR (EQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER* ) (MEMQ VARIABLE EXPORTS)))
(VALUE (ASSQ VARIABLE STATIC-VARIABLES-ALIST)))
(COND ((AND VALUE EXPORTING-P) VALUE)
((AND ;(OR (EQ EXPORTS *EXPORT-ALL-VARIABLES-MARKER* ) (MEMQ VARIABLE EXPORTS))
;allow exported variables to automatically be visible anywhere up the chain
;of exporting boxes.
(NOT-NULL EXPORTING-BOXES))
(DOLIST (BOX EXPORTING-BOXES)
(LET ((BINDING-PAIR (TELL BOX :LOOKUP-STATIC-VARIABLE-IN-EXPORTS VARIABLE)))
(WHEN (NOT-NULL BINDING-PAIR)
(RETURN BINDING-PAIR))))))))
(DEFMETHOD (BOX :LOOKUP-STATIC-VARIABLE-IN-BOX-ONLY) (VARIABLE)
(LET ((VALUE (ASSQ VARIABLE STATIC-VARIABLES-ALIST))
(EXPORTING-BOXES (TELL SELF :GET-EXPORTING-BOXES)))
(COND (VALUE VALUE)
((NOT-NULL EXPORTING-BOXES)
(DOLIST (BOX EXPORTING-BOXES)
(LET ((BINDING-PAIR (TELL BOX :LOOKUP-STATIC-VARIABLE-IN-EXPORTS VARIABLE)))
(WHEN (NOT-NULL BINDING-PAIR)
(RETURN BINDING-PAIR))))))))
(DEFMETHOD (BOX :SUPERIOR-BOX-FOR-BINDINGS) ()
(TELL SELF :SUPERIOR-BOX))
(DEFMETHOD (PORT-BOX :SUPERIOR-BOX-FOR-BINDINGS) ()
(TELL-CHECK-NIL PORTS :SUPERIOR-BOX))
(defmethod (box :lookup-static-variable-check-superiors) (variable)
(let ((value (assq variable static-variables-alist))
(EXPORTING-BOXES (TELL SELF :GET-EXPORTING-BOXES))
(superior))
(cond (value value)
;; if we found it, return it
((NOT-NULL EXPORTING-BOXES)
;; first, look in the boxes which export their variables to this box
(let ((result
(DOLIST (BOX EXPORTING-BOXES)
(LET ((BINDING-PAIR (TELL BOX
:LOOKUP-STATIC-VARIABLE-IN-EXPORTS VARIABLE)))
(WHEN (NOT-NULL BINDING-PAIR)
(RETURN BINDING-PAIR))))))
(if result result (tell (tell self :superior-box-FOR-BINDINGS)
:lookup-static-variable-check-superiors variable))))
((setq superior (tell self :superior-box-FOR-BINDINGS))
(tell superior :lookup-static-variable-check-superiors variable))
(t nil))))
(DEFMETHOD (BOX :LOCAL-LIBRARY) ()
(OR LOCAL-LIBRARY
(SETQ LOCAL-LIBRARY
(MAKE-INITIALIZED-BOX ':TYPE ':LL-BOX
':EXPORTS *EXPORT-ALL-VARIABLES-MARKER*))))
;; the file system uses this one
(DEFMETHOD (BOX :SET-LOCAL-LIBRARY) (NEW-LL)
(SETQ LOCAL-LIBRARY NEW-LL))
(DEFMETHOD (BOX :REMOVE-LOCAL-LIBRARY) ()
(WHEN (NOT-NULL LOCAL-LIBRARY)
(TELL SELF :REMOVE-ALL-STATIC-BINDINGS LOCAL-LIBRARY)
(SETQ LOCAL-LIBRARY NIL)))